package kbpost

import (
	"context"
	// "fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/blog/model"
	"mall/service/blog/rpc/internal/svc"
	"mall/service/blog/rpc/internal/utils/errorhandler"
	"mall/service/blog/rpc/types/blog"

	// "github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetKBPostByIdLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetKBPostByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetKBPostByIdLogic {
	return &GetKBPostByIdLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetKBPostByIdLogic) GetKBPostById(in *blog.BaseIdReq) (*blog.KBPostInfo, error) {
	if (in.Filter == nil || len(*in.Filter) == 0) && (in.Id == nil || *in.Id == 0) {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)
	// logrus.Info(fmt.Sprintf("GetKBPostById preloadMap: %+v", preloadMap))
	// map[blogBook:[Id Title Slug] blogBookMeta:[mediaId mediaUrl]]
	// logrus.Info(fmt.Sprintf("GetKBPostById eagerLoadMap: %+v", eagerLoadMap))
	// map[blogBook:[blogBookMeta]]

	kbPostEntityName := model.RpcEntityNames.BlogBook
	if _, ok := model.RpcEntityPreloadMap[kbPostEntityName]; !ok {
		return &blog.KBPostInfo{}, nil
	}

	kbPostRepositoryName := model.RepositoryNames.BlogBook
	columnMap, ok := model.RepositoryPreloadMap[kbPostRepositoryName]
	if !ok {
		return &blog.KBPostInfo{}, nil
	}

	blogRelationEntityName := model.RpcEntityNames.BlogBookRelation
	blogRelationSettingMap, ok := model.RpcEntityPreloadMap[blogRelationEntityName]
	if !ok {
		return &blog.KBPostInfo{}, nil
	}

	sectionMap := make(map[uint64]string) // 记录 BlogBookRelation 的全部 section name
	for _, v := range blogRelationSettingMap {
		if v.Id > 0 {
			if _, ok := sectionMap[v.Id]; !ok {
				sectionMap[v.Id] = v.Name
			}
		}
	}

	var entry *model.BlogKnowledgeBasis
	if in.Filter != nil && len(*in.Filter) > 0 {
		var selectCols []string
		if preloadFields, ok := preloadMap[kbPostEntityName]; ok {
			selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
		}
		selectCols = append(selectCols, model.BlogKnowledgeBasisColumns.PostStatus)

		filterMap := modelx.ParseFilter(in.Filter, columnMap)
		// logrus.Info(fmt.Sprintf("GetKBPostById filterMap: %+v", filterMap))
		// map[post_status:map[equalTo:[1]]]
		if filterMap == nil {
			return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
		}

		if in.Id != nil && *in.Id > 0 {
			filterMap[model.BlogKnowledgeBasisColumns.ID] = map[string][]any{
				"equalTo": {*in.Id},
			}
		}

		orderBy := modelx.ParseSorter(in.Sorter, columnMap)
		// logrus.Info(fmt.Sprintf("GetKBPostById orderBy: %s", orderBy))
		//
		if len(orderBy) == 0 {
			orderBy = model.BlogKnowledgeBasisColumns.ID + " DESC"
		}

		res, _, err := l.svcCtx.KBEntry.GetList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  orderBy,
			"select":   strings.Join(selectCols, ","),
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(res) == 0 {
			return &blog.KBPostInfo{}, nil
		}

		entry = res[0]
	} else {
		data, err := l.svcCtx.KBEntry.FindOne(l.ctx, *in.Id)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		entry = data
	}

	if entry == nil {
		return &blog.KBPostInfo{}, nil
	}

	if entry.PostStatus == uint16(globalkey.PostStatusTrash) {
		return &blog.KBPostInfo{}, nil
	}

	eid := entry.ID
	subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
	if eagerLoadEntities, ok := eagerLoadMap[kbPostEntityName]; ok {
		for _, entity := range eagerLoadEntities {
			if entitySettingMap, ok := model.RpcEntityPreloadMap[entity]; ok {
				subEntitySettingMap[entity] = entitySettingMap
			}
		}
	}

	var content *string
	blogDetailEntityName := model.RpcEntityNames.BlogBookDetail
	if _, ok := subEntitySettingMap[blogDetailEntityName]; ok {
		subFilterMap := make(map[string]map[string][]any)
		subFilterMap[model.BlogKnowledgeBaseDetailColumns.KnowledgeBaseID] = map[string][]any{
			"equalTo": {eid},
		}
		list, _, err := l.svcCtx.KBEntry.GetDetailList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  model.BlogKnowledgeBaseDetailColumns.DetailID + " DESC",
			"select":   model.BlogKnowledgeBaseDetailColumns.PostContent,
		}, -1, subFilterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			content = pointy.GetPointer(list[0].PostContent)
		}
	}

	var media []*blog.TmpMediaInfo
	var blogMediaSelectCols string
	blogMediaEntityName := model.RpcEntityNames.BlogMedia
	if _, ok := subEntitySettingMap[blogMediaEntityName]; ok {
		blogMediaRepositoryName := model.RepositoryNames.BlogMedia
		subPreloadFields, hasSubPreloadFields := preloadMap[blogMediaEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[blogMediaRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
			subSelectCols = append(subSelectCols, model.BlogAttachmentRelationshipColumns.ObjectID)
			blogMediaSelectCols = strings.Join(subSelectCols, ",")

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.BlogAttachmentRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": {eid},
			}
			subFilterMap[model.BlogAttachmentRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.AttachmentLogTypeKnowledgeBase},
			}
			orderBy := model.BlogAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," +
				model.BlogAttachmentRelationshipColumns.AttachmentOrder
			list, _, err := l.svcCtx.KBEntry.GetMedia(l.ctx, model.M{
				"orderBy": orderBy,
				"select":  strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					media = append(media, &blog.TmpMediaInfo{
						Uid:       &v.AttachmentRelationshipID,
						Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
						Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
						Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
						Uri:       &v.AttachmentSource,
						Url:       &v.AttachmentImageURL,
						AltText:   &v.AttachmentImageAlt,
						MetaData:  &v.AttachmentImageMeta,
					})
				}
			}
		}
	}

	var meta []*blog.MetaInfo
	var blogBookMetaKeys []any
	blogBookMetaKeyMap := make(map[string]string)
	kbPostMetaEntityName := model.RpcEntityNames.BlogBookMeta
	if entitySettingMap, ok := subEntitySettingMap[kbPostMetaEntityName]; ok {
		if subPreloadFields, ok := preloadMap[kbPostMetaEntityName]; ok {
			for _, field := range subPreloadFields {
				if fieldSetting, ok := entitySettingMap[field]; ok && len(fieldSetting.Name) > 0 {
					blogBookMetaKeys = append(blogBookMetaKeys, fieldSetting.Name)
					blogBookMetaKeyMap[fieldSetting.Name] = field
				}
			}

			if len(blogBookMetaKeys) > 0 {
				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.BlogKnowledgeBaseMetumColumns.KnowledgeBaseID] = map[string][]any{
					"equalTo": {eid},
				}
				subFilterMap[model.BlogKnowledgeBaseMetumColumns.MetaKey] = map[string][]any{
					"in": blogBookMetaKeys,
				}
				orderBy := model.BlogKnowledgeBaseMetumColumns.MetaID + " DESC"
				list, _, err := l.svcCtx.KBEntry.GetMeta(l.ctx, model.M{
					"orderBy": orderBy,
				}, -1, subFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(list) > 0 {
					for _, v := range list {
						key := v.MetaKey
						if len(key) > 0 && v.MetaValue.Valid {
							if field, ok := blogBookMetaKeyMap[key]; ok {
								meta = append(meta, &blog.MetaInfo{
									Id:    &v.MetaID,
									Key:   pointy.GetPointer(field),
									Value: pointy.GetPointer(v.MetaValue.String),
								})
							}
						}
					}
				}
			}
		}
	}

	var categories []*blog.TermInfo
	var tags []*blog.TermInfo
	var sections []*blog.TermInfo
	blogTermEntityName := model.RpcEntityNames.BlogTerm
	if _, ok := subEntitySettingMap[blogTermEntityName]; ok {
		blogTermRepositoryName := model.RepositoryNames.BlogTerm
		subPreloadFields, hasSubPreloadFields := preloadMap[blogTermEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[blogTermRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.BlogTermRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": {eid},
			}
			subFilterMap[model.BlogTermRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.PostTypeKnowledgeBase},
			}
			list, _, err := l.svcCtx.Term.GetRelationshipList(l.ctx, model.M{
				"select": model.BlogTermRelationshipColumns.TermID,
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				var tids []any
				for _, v := range list {
					tids = append(tids, v.TermID)
				}

				termFilterMap := make(map[string]map[string][]any)
				termFilterMap[model.BlogTermColumns.TermID] = map[string][]any{
					"in": tids,
				}
				subSelectCols = append(subSelectCols, model.BlogTermColumns.Taxonomy) // 必须获取以区分category及tag
				termList, _, err := l.svcCtx.Term.GetList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, termFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(termList) > 0 {
					for _, v := range termList {
						if v.Taxonomy == uint16(globalkey.TermTaxonomyCategory) {
							categories = append(categories, &blog.TermInfo{
								Id:   &v.TermID,
								Name: &v.Name,
								Slug: &v.Slug,
							})
						} else if v.Taxonomy == uint16(globalkey.TermTaxonomyTag) {
							tags = append(tags, &blog.TermInfo{
								Id:   &v.TermID,
								Name: &v.Name,
								Slug: &v.Slug,
							})
						} else if v.Taxonomy == uint16(globalkey.TermTaxonomyKnowledgeBase) {
							sections = append(sections, &blog.TermInfo{
								Id:   &v.TermID,
								Name: &v.Name,
								Slug: &v.Slug,
							})
						}
					}
				}
			}
		}
	}

	var relations []*blog.KBPostInfo
	if _, ok := subEntitySettingMap[blogRelationEntityName]; ok {
		blogRelationRepositoryName := model.RepositoryNames.BlogBookRelation
		subPreloadFields, hasSubPreloadFields := preloadMap[blogRelationEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[blogRelationRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			entityMap := make(map[string]struct{})
			selectColMap := make(map[string]string)
			for _, field := range subPreloadFields {
				if _, ok := entityMap[field]; !ok {
					entityMap[field] = struct{}{}
					if column, ok := subColumnMap[field]; ok {
						selectColMap[field] = column
					} else {
						selectColMap[field] = ""
					}
				}
			}

			var relationIds []any
			for _, v := range blogRelationSettingMap {
				if v.Id > 0 {
					if _, ok := entityMap[v.Name]; ok {
						relationIds = append(relationIds, v.Id)
					}
				}
			}

			if len(relationIds) > 0 {
				var bothWay bool // 正向查询
				for _, v := range blogRelationSettingMap {
					if v.Id == entry.SectionID {
						bothWay = v.BothWay
						break
					}
				}

				var subSelectCols []string
				for _, column := range selectColMap {
					if len(column) > 0 {
						subSelectCols = append(subSelectCols, column)
					}
				}

				tidMap := make(map[uint64]uint32)
				var tids []any
				if bothWay { // 香水获取关联的品牌是正向，品牌获取关联的香水要反向。
					filterMap := make(map[string]map[string][]any)
					filterMap[model.BlogKnowledgeBaseRelationshipColumns.FromEntryID] = map[string][]any{
						"equalTo": {eid},
					}
					filterMap[model.BlogKnowledgeBaseRelationshipColumns.ToSectionID] = map[string][]any{
						"in": relationIds,
					}
					list, _, err := l.svcCtx.KBEntry.GetRelationshipList(l.ctx, model.M{
						"select":  model.BlogKnowledgeBaseRelationshipColumns.ToEntryID + "," + model.BlogKnowledgeBaseRelationshipColumns.ToEntryGroup,
						"orderBy": model.BlogKnowledgeBaseRelationshipColumns.ToEntryOrder,
					}, -1, filterMap)
					if err != nil {
						return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
					}

					if len(list) > 0 {
						for _, v := range list {
							if _, ok := tidMap[v.ToEntryID]; !ok {
								tidMap[v.ToEntryID] = uint32(v.ToEntryGroup)
								tids = append(tids, v.ToEntryID)
							}
						}
					}
				} else {
					filterMap := make(map[string]map[string][]any)
					filterMap[model.BlogKnowledgeBaseRelationshipColumns.ToEntryID] = map[string][]any{
						"equalTo": {eid},
					}
					filterMap[model.BlogKnowledgeBaseRelationshipColumns.FromSectionID] = map[string][]any{
						"in": relationIds,
					}
					list, _, err := l.svcCtx.KBEntry.GetRelationshipList(l.ctx, model.M{
						"select":  model.BlogKnowledgeBaseRelationshipColumns.FromEntryID,
						"orderBy": model.BlogKnowledgeBaseRelationshipColumns.EntryRelationshipID,
					}, -1, filterMap)
					if err != nil {
						return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
					}

					if len(list) > 0 {
						for _, v := range list {
							if _, ok := tidMap[v.FromEntryID]; !ok {
								tidMap[v.FromEntryID] = 0
								tids = append(tids, v.FromEntryID)
							}
						}
					}
				}

				if len(tids) > 0 {
					filterMap := make(map[string]map[string][]any)
					filterMap[model.BlogKnowledgeBasisColumns.ID] = map[string][]any{
						"in": tids,
					}
					filterMap[model.BlogKnowledgeBasisColumns.PostStatus] = map[string][]any{
						"equalTo": {globalkey.PostStatusPublish},
					}
					var entrySelectCols []string
					if len(subSelectCols) > 0 {
						entrySelectCols = subSelectCols[:]
					} else {
						entrySelectCols = []string{
							model.BlogKnowledgeBasisColumns.ID,
							model.BlogKnowledgeBasisColumns.SectionID,
							model.BlogKnowledgeBasisColumns.PostTitle,
							model.BlogKnowledgeBasisColumns.PostName,
						}
					}
					list, _, err := l.svcCtx.KBEntry.GetList(l.ctx, model.M{
						"select": strings.Join(entrySelectCols, ","),
					}, -1, filterMap)
					if err != nil {
						return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
					}

					if len(list) > 0 {
						var eids []any
						for _, v := range list {
							eids = append(eids, v.ID)
						}

						mediaMap := make(map[uint64]*blog.TmpMediaInfo)
						subFilterMap := make(map[string]map[string][]any)
						subFilterMap[model.BlogAttachmentRelationshipColumns.ObjectID] = map[string][]any{
							"in": eids,
						}
						subFilterMap[model.BlogAttachmentRelationshipColumns.ObjectType] = map[string][]any{
							"equalTo": {globalkey.AttachmentLogTypeKnowledgeBase},
						}
						subFilterMap[model.BlogAttachmentRelationshipColumns.AttachmentThumbnail] = map[string][]any{
							"equalTo": {1},
						}
						mediaList, _, err := l.svcCtx.KBEntry.GetMedia(l.ctx, model.M{
							"select": blogMediaSelectCols,
						}, -1, subFilterMap)
						if err != nil {
							return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
						}

						if len(mediaList) > 0 {
							for _, v := range mediaList {
								if _, ok := mediaMap[v.ObjectID]; !ok {
									mediaMap[v.ObjectID] = &blog.TmpMediaInfo{
										Uid:       &v.AttachmentRelationshipID,
										Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
										Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
										Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
										Uri:       &v.AttachmentSource,
										Url:       &v.AttachmentImageURL,
										AltText:   &v.AttachmentImageAlt,
										MetaData:  &v.AttachmentImageMeta,
									}
								}
							}
						}

						metaMap := make(map[uint64][]*blog.MetaInfo)
						if len(blogBookMetaKeys) > 0 {
							subMetaFilterMap := make(map[string]map[string][]any)
							subMetaFilterMap[model.BlogKnowledgeBaseMetumColumns.KnowledgeBaseID] = map[string][]any{
								"in": eids,
							}
							subMetaFilterMap[model.BlogKnowledgeBaseMetumColumns.MetaKey] = map[string][]any{
								"in": blogBookMetaKeys,
							}
							orderBy := model.BlogKnowledgeBaseMetumColumns.MetaID + " DESC"
							metaList, _, err := l.svcCtx.KBEntry.GetMeta(l.ctx, model.M{
								"orderBy": orderBy,
							}, -1, subMetaFilterMap)
							if err != nil {
								return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
							}

							if len(metaList) > 0 {
								for _, v := range metaList {
									key := v.MetaKey
									if len(key) > 0 && v.MetaValue.Valid {
										if field, ok := blogBookMetaKeyMap[v.MetaKey]; ok {
											metaMap[v.KnowledgeBaseID] = append(metaMap[v.KnowledgeBaseID], &blog.MetaInfo{
												Id:    &v.MetaID,
												Key:   pointy.GetPointer(field),
												Value: pointy.GetPointer(v.MetaValue.String),
											})
										}
									}
								}
							}
						}

						for _, v := range list {
							var section *string
							if s, ok := sectionMap[v.SectionID]; ok {
								section = pointy.GetPointer(s)
							}
							var groupId *uint64
							if gid, ok := tidMap[v.ID]; ok {
								groupId = pointy.GetPointer(uint64(gid))
							}
							var media []*blog.TmpMediaInfo
							if ms, ok := mediaMap[v.ID]; ok {
								media = append(media, ms)
							}
							var meta []*blog.MetaInfo
							if ms, ok := metaMap[v.ID]; ok {
								meta = ms[:]
							}
							relations = append(relations, &blog.KBPostInfo{
								Id:             &v.ID,
								Title:          &v.PostTitle,
								Slug:           &v.PostName,
								SectionId:      &v.SectionID,
								SectionGroupId: groupId,
								Section:        section,
								Meta:           meta,
								Media:          media,
							})
						}
					}
				}
			}
		}
	}

	if in.SearchLogId != nil {
		var visitorId uint64
		if in.VisitorId != nil {
			visitorId = *in.VisitorId
		}
		var platformId uint
		if in.PlatformId != nil {
			platformId = uint(*in.PlatformId)
		}
		l.svcCtx.User.InsertClickLog(l.ctx, nil, &model.BlogUserClickLog{
			UserID:      visitorId,
			PlatformID:  platformId,
			SearchLogID: *in.SearchLogId,
			ObjectID:    eid,
			ObjectType:  uint16(globalkey.PostTypeKnowledgeBase),
			CreatedAt:   time.Now(),
		})
	}

	return &blog.KBPostInfo{
		Id:            pointy.GetPointer(eid),
		Title:         &entry.PostTitle,
		Slug:          &entry.PostName,
		Content:       content,
		Excerpt:       &entry.PostExcerpt,
		CommentStatus: pointy.GetPointer(uint32(entry.CommentStatus)),
		Uri:           &entry.GUID,
		ParentId:      &entry.PostParentID,
		SectionId:     &entry.SectionID,
		Password:      &entry.PostPassword,
		PublicDate:    pointy.GetPointer(modelx.FormatTime(entry.PublicDate, "")),
		CreatedAt:     pointy.GetPointer(modelx.FormatTime(entry.PostDate, "")),
		UpdatedAt:     pointy.GetPointer(modelx.FormatTime(entry.PostModifiedDate, "")),
		Sort:          pointy.GetPointer(uint32(entry.MenuOrder)),
		Meta:          meta,
		Media:         media,
		Categories:    categories,
		Tags:          tags,
		Sections:      sections,
		Relations:     relations,
	}, nil
}
